package controller

import (
	"elegantGo/chapter17/controller/request"
	"elegantGo/chapter17/controller/response"
	"elegantGo/chapter17/oo"
	"elegantGo/chapter17/pkg/slices"
	"elegantGo/chapter17/repository"
	"elegantGo/chapter17/repository/ent"
	"elegantGo/chapter17/repository/ent/article"
	"elegantGo/chapter17/repository/ent/articleorder"
	"elegantGo/chapter17/service"
	"github.com/gin-gonic/gin"
)

type Article struct {
	repo     *repository.Article
	repoUser *repository.User
}

func NewArticle(repo *repository.Article, repoUser *repository.User) *Article {
	ctr := new(Article)
	ctr.repo = repo
	ctr.repoUser = repoUser
	return ctr
}

func (ctr *Article) One(c *gin.Context) (response.Data, error) {
	req := new(request.ArticleOne)
	if err := c.ShouldBind(req); err != nil {
		return nil, err
	}

	sessionUser := ctr.repoUser.Fetch(c.Request.Context(), req.UserID)

	ooArticle := ctr.repo.FetchOne(c.Request.Context(), func(articleQuery *ent.ArticleQuery) {
		articleQuery.WithUser().Where(article.ID(req.ID))
		if sessionUser != nil {
			articleQuery.WithOrders(func(articleOrderQuery *ent.ArticleOrderQuery) {
				articleOrderQuery.Where(articleorder.ID(sessionUser.GetID()))
			})
		}
	})

	if ooArticle == nil {
		return nil, nil
	}

	srvUserArticlePermission := service.NewUserArticlePermission(sessionUser, ooArticle)
	if !srvUserArticlePermission.Readable() {
		if contentMasker, ok := ooArticle.(oo.ArticleContentMasker); ok {
			contentMasker.Mask()
		}
	}

	if payPerArticle, ok := ooArticle.(*oo.PaidArticle); ok {
		articleDiscountPriceCalculator := service.NewArticleDiscountPriceCalculator(sessionUser)
		payPerArticle.DiscountPriceCalculate(articleDiscountPriceCalculator.Calculate)
	}

	return ooArticle.Mapper(), nil
	//return ooArticle.MapperWithMask(oo.NewFieldMask("User{ID}")), nil
}

func (ctr *Article) Many(c *gin.Context) (response.Data, error) {
	ooArticles := ctr.repo.FetchMany(c.Request.Context(), func(opt *ent.ArticleQuery) {
		opt.WithUser().Select(slices.Remove(article.Columns, article.FieldContent)...)
	})
	return ooArticles.Mapper(), nil
}

func (ctr *Article) Create(c *gin.Context) (response.Data, error) {
	req := new(request.ArticleCreate)
	if err := c.ShouldBind(req); err != nil {
		return nil, err
	}

	cmdArticle := oo.NewArticle(req.Category)
	cmdArticle.SetTitle(req.Title)
	cmdArticle.SetContent(req.Content)
	if payPerArticle, ok := cmdArticle.(*oo.PaidArticle); ok {
		payPerArticle.SetPrice(req.Price)
	}

	ooArticle := ctr.repo.Save(c.Request.Context(), cmdArticle)
	return ooArticle.Mapper(), nil
}
